package raft

import (
	"encoding/json"
	"fmt"
	"os"
	"sync"
)

type Persister interface {
	SaveState(term int64, votedFor int32, logs []*LogEntry, lastSnapshotIndex, lastSnapshotTerm int64)
	LoadState() (int64, int32, []*LogEntry, int64, int64, bool)
	SaveSnapshot(snapshot []byte, lastIndex, lastTerm int64)
	LoadSnapshot() ([]byte, int64, int64, error)
}

type FilePersister struct {
	mu           sync.Mutex
	stateFile    string
	snapshotFile string
}

func NewFilePersister(id int) Persister {
	return &FilePersister{
		stateFile:    fmt.Sprintf("raft-state-%d.json", id),
		snapshotFile: fmt.Sprintf("raft-snapshot-%d.bin", id),
	}
}

func (fp *FilePersister) SaveState(term int64, votedFor int32, logs []*LogEntry, lastSnapshotIndex, lastSnapshotTerm int64) {
	fp.mu.Lock()
	defer fp.mu.Unlock()

	state := struct {
		Term              int64
		VotedFor          int32
		Logs              []*LogEntry
		LastSnapshotIndex int64
		LastSnapshotTerm  int64
	}{
		term,
		votedFor,
		logs,
		lastSnapshotIndex,
		lastSnapshotTerm,
	}

	data, _ := json.Marshal(state)
	os.WriteFile(fp.stateFile, data, 0644)
}

func (fp *FilePersister) LoadState() (int64, int32, []*LogEntry, int64, int64, bool) {
	fp.mu.Lock()
	defer fp.mu.Unlock()

	data, err := os.ReadFile(fp.stateFile)
	if err != nil {
		return 0, -1, nil, 0, 0, false
	}

	var state struct {
		Term              int64
		VotedFor          int32
		Logs              []*LogEntry
		LastSnapshotIndex int64
		LastSnapshotTerm  int64
	}

	if err := json.Unmarshal(data, &state); err != nil {
		return 0, -1, nil, 0, 0, false
	}

	return state.Term, state.VotedFor, state.Logs, state.LastSnapshotIndex, state.LastSnapshotTerm, true
}

func (fp *FilePersister) SaveSnapshot(snapshot []byte, lastIndex, lastTerm int64) {
	fp.mu.Lock()
	defer fp.mu.Unlock()

	os.WriteFile(fp.snapshotFile, snapshot, 0644)

	// 更新快照元数据
	if lastIndex > 0 {
		meta := struct {
			LastIndex int64
			LastTerm  int64
		}{
			lastIndex,
			lastTerm,
		}

		metaData, _ := json.Marshal(meta)
		os.WriteFile(fp.snapshotFile+".meta", metaData, 0644)
	}
}

func (fp *FilePersister) LoadSnapshot() ([]byte, int64, int64, error) {
	fp.mu.Lock()
	defer fp.mu.Unlock()

	snapshot, err := os.ReadFile(fp.snapshotFile)
	if err != nil {
		return nil, 0, 0, err
	}

	metaData, err := os.ReadFile(fp.snapshotFile + ".meta")
	if err != nil {
		return snapshot, 0, 0, nil
	}

	var meta struct {
		LastIndex int64
		LastTerm  int64
	}

	if err := json.Unmarshal(metaData, &meta); err != nil {
		return snapshot, 0, 0, nil
	}

	return snapshot, meta.LastIndex, meta.LastTerm, nil
}
